
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "cmsis_os2.h"                          // CMSIS RTOS header file
 
/*----------------------------------------------------------------------------
 *      Thread 1 'Thread_Name': Sample thread
 
 不在Thread_Disp()驱动DAC，因为显示频率低。更新DAC需要和ADC同步（同频率）。
 *---------------------------------------------------------------------------*/
#include "dbgUart/dbgUart.h" 
#include "bsp.h"
#include "key.h"
#include "menu_seg.h"
#include "threads.h"
#include "parameter.h"
#include "preset_point.h"
#include "drv_ch423s.h"
#include "Thread_disp.h"
#include "weight_polyline.h"
#include "LED.h"


#define SLEEP_TMO		(5*60*1000)		//当SLEEP_TMO时间内无按键，进入熄屏模式。


extern uint8_t in_sta;							//IN1状态：1---有效（valide），0---无效（invalid）。
extern uint8_t flag_type;
extern uint8_t flag_para_ok;
extern int32_t para_to_flash_2(uint32_t );
extern uint16_t CRC16(uint8_t *puchMsg, uint16_t usDataLen);
extern uint8_t flag_para_update_fir;






PRESET_P preset_point[5];	//[0]不使用，使用[1~4]。

static int32_t norm_in1_deal(MSG_U msg, int32_t wt);
static void seg_pwr_on_disp(void);
static void do_ser_cal(MSG_U* msg);

static int32_t prepoint_satisfy(uint32_t n, int32_t val);
static void prepoint_sta(uint32_t n, int32_t val);

static void spoint_config(void);
static void zz_det_init(void);
static void zz_det_update(ZZONE_DET* p, int32_t wt);


#ifdef OFFSET_DIV_N_F
float offset_div_n_f_man;				//手动触发
float offset_div_n_f_pwr_on;			//上电自动清零
float offset_div_n_f_trace;				//零点跟踪
float offset_div_n_f_tare;				//去皮
#else
int32_t offset_man;			//手动触发				在显示模式下，按下zero键；显示值在清零范围。			-1*显示值 --> offset
int32_t	offset_pwr_on;		//上电自动清零			上电；上电清零开关有效；显示值在清零范围。				-1*显示值 --> offset_pwr_on
int32_t	offset_trace;		//零点跟踪				显示值在跟踪范围；持续时间超过1.6秒。					-1*显示值 --> offset_zero_range
#endif

SLEEP sleep;							//成员变量默认为0。
uint8_t flag_clear_sleep_off;			//是否清零SLEEP结构体。
uint8_t flag_NOT_in_menu_mode;			//是否在MENU_MODE，是为1，不是为0。在MENU_MODE时，不实时处理OUT1，OUT2。
uint8_t flag_disp_norm;					//是否完成上电显示Logo等操作。

typedef enum{
	DISP_NORM,
	DISP_CONF,			//配置频率模式。
	DISP_ERR, 
	DISP_MENU, 
	DISP_ANA, 
	DISP_HOLD,
	DISP_HOLD_WT,		//准备作废。
	DISP_CAL,			//标定模式（显示mv值）。因为只有在Thread_Disp()中能获得mV和称重值。
	DISP_SLEEP,			//省电模式：数码管显示"-     "。
}DISP_MODE;				//0-based. 数码管的显示模式（简称显示模式，区别于单个seg的显示模式：SEG_MODE）。

DISP_MODE disp_mode= DISP_NORM;

WT_DET wt_det={.sta= STA_LAJI_IDLE, .ts_pre= -1};


/*菜单项目
*/ 
void Thread_Disp (void *argument);                   // thread function
 

int Init_Thread_Disp (void) {
	osThreadId_t tid_Thread;                        // thread id
	
	const osThreadAttr_t attr={.stack_size= 2048};                       // thread id 
	tid_Thread = osThreadNew(Thread_Disp, NULL, &attr);
	if (tid_Thread == NULL) {
		return(-1);
	}
 
	return(0);
}


//extern int32_t offset_disp;		//显示时的偏移量。



__NO_RETURN void Thread_Disp (void *argument) {
	(void)argument;	//Prevent unused argument(s) compilation warning.
	
	char str[128];
	char str_ts[20];	//含'\0'的个数。
	char str_wt[20];		//含'\0'的个数和可能的1小数点，例："12.3"，最长"12345.6"。
	char str_gps[256];	//含'\0'的个数。
	uint8_t sta_cal;
	uint32_t ts_stab;		//稳定开始时间戳
	uint32_t ts_cal_s;		//标定过程开始时间戳。
	
	int32_t wt;
	int32_t wt_div_n;
	osStatus_t ret;
	uint16_t key;				//从消息队列中获取的按键值。
	MSG_U msg;					//组合消息
	
	menu_init();
	//
	//bsp_seg_hal();			//使用CH423S，不再直接驱动数码管。
	while(0 == flag_para_ok){
		osDelay(100);
	}
	
	uartS("\nThread_disp(N) start...\n");
	
	LS_string("1.0.0");	//显示软件版本号。
	
	
	disp_mode= DISP_NORM;
	flag_NOT_in_menu_mode= 1;
	flag_disp_norm= 1;
	
	
	while (1) {
		//接收按键消息。
		ret= osMessageQueueGet(mid_key_disp_io, &msg, 0, 10);
		
		if(osOK != ret){
			//无消息时的超时处理。!!不能去掉。
			continue;			
		}
		
		if(MSG_TYPE_MV_WT == msg.type){
			wt= msg.wt;					//更新称重值，以便后用。
			
			if(dbg_sw & DBG_THREAD_DISP)			
				uartS("msg_mv_wt");			
		}
		else{
			if(dbg_sw & DBG_THREAD_DISP)
				uartS("msg_key");		//线程Thread_In()定时发送In1状态，In1也按msg_key处理。
			
			//处理重启虚拟按键。
			if(VKEY_RST == msg.key){
				dbg_flag.rst= 1;
				dbg_flag.p1= 88;
			}
		}
		
		
		if(DISP_CAL == disp_mode){	//标定模式（显示mv值）
		}			
		else if(DISP_NORM == disp_mode){			//称重模式（正常显示模式）
			
			/*更新显示：由称重值驱动。
			*/
			if(MSG_TYPE_MV_WT == msg.type){
				wt_to_disp_buf((char*)msg.dat);		//将实时称重值更新到显示缓冲区。（即可以显示了）
			}
			else if(MSG_TYPE_KEY == msg.type){
				/*称重模式下的顶层菜单按键处理：
				K1_ESC:		
				K2_OPT:		
				K3_MOD:
				K4_ENTER:
				*/
				key= msg.key;
				
				if(KEY_K2_MODE == key){
					//获取第1个菜单项，并显示。
					cur_item= get_first_item();
					if(cur_item->f_enter != NULL)
						cur_item->f_enter(msg);
					
					//seg_updata(cur_item->content);
					disp_mode= DISP_MENU;
					
					uartS("\nenter menu for MODE");
				}
				else if(KEY_K1_OPT == key){
					//获取第1个菜单项，并显示。
					cur_item= get_first_item();
					if(cur_item->f_enter != NULL)
						cur_item->f_enter(msg);
					
					//seg_updata(cur_item->content);
					disp_mode= DISP_MENU;
					
					uartS("\nenter menu for OPT");
				}
				else if(KEY_K6_ESC == key){
				}				
				else if(KEY_K5_ENTER == key){
				}
				else{}
			}	
		}
		else if(DISP_MENU == disp_mode){	//菜单模式
			
			flag_NOT_in_menu_mode= 0;
			
			if(MSG_TYPE_KEY == msg.type){
				key= msg.key;
				
				if((NULL == cur_item->parent) && (KEY_K6_ESC == key)){	//cur_item指向setup...cal，有ESC按键，则退出DISP_MENU。只有setup...菜单项的parent是NULL，即它们是顶层菜单。
					disp_mode= DISP_NORM;
					//mode_mv_wt= MODE_WT;		//Thread_ADC()发送称重值（非uV值）。
					
					flag_NOT_in_menu_mode= 1;
					
					uartS("\nMENU to NORM");
					LS_string("--------");
				}
				else{
					if(cur_item->f_key != NULL)
						cur_item->f_key(msg);	//!!每个菜单项有自己的按键处理函数，主要是菜单项指针的更改。
					else{
						uartS("\n$err:no f_key");
					}
				}
			}
			else{
				//菜单项处理非按键消息（称重消息，mv消息等）。
				if(cur_item->f_key != NULL)
					cur_item->f_key(msg);		//!!每个菜单项有自己的按键处理函数，可能还要处理非按键消息（例，称重值...）。
			}
		}
		else if(DISP_ERR == disp_mode){		//错误显示模式（准备作废）
			//错误显示模式
		}
	}
}



/*正常显示模式下，对in1消息的处理函数。
参数 msg：
参数 wt: 当前称重值（是上1个称重消息中的称重值）。
依据para_xxx.in1_define进行处理：
para_xxx.in1_define		处理方法
	1					对显示清零
	2					对限制1的状态清零
	3					对限制2的状态清零
	4					对限制3的状态清零
	5					对限制4的状态清零
	6					触发保持
	7					电平保持
*/
static int32_t norm_in1_deal(MSG_U msg, int32_t wt)
{
	uint16_t key;
	int32_t ret;
	char str[64];
	
	key= msg.key;
	
	
	if(MSG_TYPE_KEY != msg.type)
		return 0;
	
	
	if(0 == strcmp(ITEM_IN_0, _tbl_in[para_xxx.in1_define_idx])){
		//无定义，即不使用in1。
	}
	else if(0 == strcmp(ITEM_IN_1, _tbl_in[para_xxx.in1_define_idx])){
		//对显示清零
		if(KEY_IN1_TO_VALID == key)
			flag_zero_man= 1;
	}
	else if(0 == strcmp(ITEM_IN_2, _tbl_in[para_xxx.in1_define_idx])){
	
	}
	else if(0 == strcmp(ITEM_IN_3, _tbl_in[para_xxx.in1_define_idx])){
	
	}
	else if(0 == strcmp(ITEM_IN_4, _tbl_in[para_xxx.in1_define_idx])){
	
	}
	else if(0 == strcmp(ITEM_IN_5, _tbl_in[para_xxx.in1_define_idx])){
	
	}
	else if(0 == strcmp(ITEM_IN_6, _tbl_in[para_xxx.in1_define_idx])){	//处理保持功能。
//		if(KEY_IN1_TO_VALID == key){	//切换保持功能下的触发状态。
//			hold_trig_sta= !hold_trig_sta;	
//			
//			sprintf(str, "\nhold_trig_sta= %d", hold_trig_sta);
//			uartS(str);
//		}
	}
	else if(0 == strcmp(ITEM_IN_7, _tbl_in[para_xxx.in1_define_idx])){	//处理保持功能。
	
	}
	else if(0 == strcmp(ITEM_IN_8, _tbl_in[para_xxx.in1_define_idx])){	//垃圾车应用
		/*几种测试场景：
		a. 将铁物贴近接近开关，上电。快速移开铁物后再贴近。这时会忽略这个检测，因为.ts_pre=0，时间间隔太小。
		b. 再显示保持时，贴近接近开关。显示保持状态，不处理接近开关消息。
		c. 
		*/
		if(KEY_IN1_TO_VALID == key){
			uint32_t ts= ts_get();
			uint32_t ts_delta= ts_del_o_n(wt_det.ts_pre, ts);
			
			if(ts_delta< (_tbl_int_interval_min[para_self.up_dn_interval_min_idx]*1000)){
				//间隔太小，忽略本次检测点（认为本次检测点是误动点）。
				sprintf(str, "\nignore this IN1's valid:%d", ts);
				if(dbg_sw & DBG_LAJI)
					uartS(str);
				
				return ERR_IN_IGNORE;	//!!因为忽略，所以不更新wt_det.ts_pre。
			}
			
			
			switch (wt_det.sta){
				case STA_LAJI_IDLE:	//复位状态下，忽略所有IN有效。
					if(dbg_sw & DBG_LAJI)
						uartS("\n$err:IN1 valid in STA_IDLE");
					
					ret= ERR_IN_LAJI_IGNORE_IDLE;
					
					break;
				
				case STA_LAJI_WAIT_UP:
					//拍照（时间戳，称重值）
					wt_det.ts_up= ts;
					wt_det.wt_up= wt;
					curve.flag_step= 2;	//告之ADC追加一个大值。
					
					wt_det.ts_pre= ts;	//更新“IN1有效”时间戳。
					
					wt_det.sta= STA_LAJI_WAIT_DN;
					wt_det.to_mode= LAJI_SHOW_UP;	//要保持显示上升称重值。当处于LAJI_SHOW_UP时，保持时间到，自动切换为LAJI_SHOW_G，再保持时间到，自动切换为LAJI_SHOW_0。
					
					if(dbg_sw & DBG_LAJI){
						sprintf(str, "\n$WAIT_UP --> WAIT_DN, ts=%d,wt=%d",wt_det.ts_up, wt_det.wt_up);
						uartS(str);
					}
					
					ret= ERR_IN_LAJI_UP;
					
					break;
					
				case STA_LAJI_WAIT_DN:
					//判断触发是否超时，如果超时，则认为是UP。
					if((_tbl_int_interval_max[para_self.up_dn_interval_max_idx]*1000)< ts_delta ){
						if(dbg_sw & DBG_LAJI){
							sprintf(str, "\n$tmo:WAIT_DN --> WAIT_DN, ts_pre=%d,ts=%d",wt_det.ts_pre, ts);
							uartS(str);
						}
						
						//拍照（时间戳，称重值）
						wt_det.ts_up= ts;
						wt_det.wt_up= wt;
						curve.flag_step= 2;	//告之ADC追加一个大值。
						
						wt_det.ts_pre= ts;	//更新“IN1有效”时间戳。
						
						wt_det.sta= STA_LAJI_WAIT_DN;
						wt_det.to_mode= LAJI_SHOW_UP;	//要保持显示上升称重值
						
						ret= ERR_IN_LAJI_TMO_UP;
					}
					else{
						//认可是下降采集，拍照（时间戳，称重值）	
						wt_det.ts_dn= ts;
						wt_det.wt_dn= wt;
						curve.flag_step= 3;	//告之ADC追加一个小值。
						wt_det.wt_net= wt_det.wt_up- wt_det.wt_dn;
						
						wt_det.ts_pre= ts;	//更新“IN1有效”时间戳。
					
						wt_det.sta= STA_LAJI_WAIT_ZZ;
						wt_det.to_mode= LAJI_SHOW_DN;	//要保持显示下降称重值
						
						if(dbg_sw & DBG_LAJI){
							sprintf(str, "\n$WAIT_DN --> WAIT_ZZ, ts=%d,wt=%d, nw= %d",wt_det.ts_dn, wt_det.wt_dn, wt_det.wt_net);
							uartS(str);
						}
						
						ret= ERR_IN_LAJI_NET;
						
						/*发送数据。
						*/
						uartS("\nsend data to 4G module\n\n\n");
						
						osEventFlagsSet(evt_id_dtu, 0x00000001U);			//待ISR完成后，再做可能的调度。
					}
					
					break;
				
				case STA_LAJI_WAIT_ZZ:
					/*等待进入_LAJI_IDLE。正常状态，当卸下垃圾桶后，由零区状态机，会强制为STA_LAJI_IDLE。
					在此状态下收到IN有效，暂不处理，认为是误动。
					*/
					if(dbg_sw & DBG_LAJI)
						uartS("\n$err:IN1 valid in WAIT_ZZ");
					
					ret= ERR_IN_LAJI_IGNORE_ZZ;
					
					break;
				
				default:
					if(dbg_sw & DBG_LAJI)
						uartS("\n$err:STA unkown");
					
					ret= ERR_IN_LAJI_IGNORE_UNKOWN;
					
					break;
			}
			
			return ret;
		}//end of if(KEY_IN1_TO_VALID == key).
		
		return ERR_IN_LAJI_INVALID;
	}
	else
		uartS("\n unkown in1_define");
	
	return 0;
}



///*【准备作废】根据输入（显示值，IN1状体），更改预置点状态。
//返回值：0
//输入参数 wt: 待显示的称重值
//*/
//static int32_t preset_point_deal(int32_t wt)
//{
//	if(( para_xxx.out1_define< 3 ) && (para_xxx.out2_define< 3)){
//		//不使用限制1~4，则直接退出。
//		return 0;
//	}
//	
//	
//	//处理OUT1
//	if(0 == strcmp(ITEM_DIG_03, _tbl_out[para_xxx.out1_define_idx])){
//		//使用限制1	
//	}
//	else if(0 == strcmp(ITEM_DIG_04, _tbl_out[para_xxx.out1_define_idx])){
//		//使用限制2
//	}
//	else if(0 == strcmp(ITEM_DIG_05, _tbl_out[para_xxx.out1_define_idx])){
//		//使用限制3
//	}
//	else if(0 == strcmp(ITEM_DIG_06, _tbl_out[para_xxx.out1_define_idx])){
//		//使用限制4
//	}
//	else
//		uartS("\nunkown out1_define");
//	
//	
//	//处理OUT2
//	if(0 == strcmp(ITEM_DIG_03, _tbl_out[para_xxx.out2_define_idx])){
//		//使用限制1
//		if(preset_point[1].flag_wait_unsatisfy){
//		
//		}
//			
//	}
//	else if(0 == strcmp(ITEM_DIG_04, _tbl_out[para_xxx.out2_define_idx])){
//		//使用限制2
//	}
//	else if(0 == strcmp(ITEM_DIG_05, _tbl_out[para_xxx.out2_define_idx])){
//		//使用限制3
//	}
//	else if(0 == strcmp(ITEM_DIG_06, _tbl_out[para_xxx.out2_define_idx])){
//		//使用限制4
//	}	
//	else
//		uartS("\nunkown out2_define");
//	
//	return 0;

//}



/*将wt输入后，看是否满足指定预置点的条件。
返回值：0---满足，其他---不满足
输入参数 wt: 称重值（待显示的）
输入参数 idx: 预置点序号，有效范围[1,4]。
!!暂不支持外部触发。
*/
static int32_t preset_point_satisfy(int32_t wt, int32_t idx)
{
	if((0 == para_xxx.preset_p1_condition) && (1 == idx)) 
		return -1;	//禁止（当前不满足）
	
	if((0 == para_xxx.preset_p2_condition) && (2 == idx)) 
		return -1;	//禁止（当前不满足）
	
	if((0 == para_xxx.preset_p3_condition) && (3 == idx)) 
		return -1;	//禁止（当前不满足）
	
	if((0 == para_xxx.preset_p4_condition) && (4 == idx)) 
		return -1;	//禁止（当前不满足）
	
	if(1 == idx){
		if(1 == para_xxx.preset_p1_condition){
			//< 
		}
		else if(2 == para_xxx.preset_p1_condition){
			//<= 
		}
		else if(3 == para_xxx.preset_p1_condition){
			//== 
		}
		else if(4 == para_xxx.preset_p1_condition){
			//>= 
		}
		else if(5 == para_xxx.preset_p1_condition){
			//> 
		}
		else if(6 == para_xxx.preset_p1_condition){
			//!= 
		}
		else if(7 == para_xxx.preset_p1_condition){
			//区间外
		}
		else if(8 == para_xxx.preset_p1_condition){
			//区间内（含区间值）
		}
	}
	
	return 0;
}



/*上电显示秒闪2次"8.8.8.8.8.8."，再显示"0ForcE"1秒。
*/
static void seg_pwr_on_disp(void)
{
	osStatus_t ret;
	uint16_t key;				//从消息队列中获取的按键值。
	MSG_U msg;					//组合消息
	uint32_t cnt= 0;
	uint32_t cp;
	int32_t icp;
	uint32_t ts,ts_2;
	char str[64];
	
//	cp= para_xxx.fs_max_hi;
//	cp<<= 16;
//	cp|= para_xxx.fs_max_lo;
//	
//	icp= (int32_t)cp;	//最大量程
	
	
	dbg_sw|= DBG_BUZ;	
	buz_beep(100);
	
//	for(int i= 0; i< 3; i++){
//		
//		seg_disp_8_dot();
//		osDelay(250);
//		
//		seg_disp_blank();
//		osDelay(250);
//	}
//	
//	buz_beep(100);
//	seg_disp_logo();
//	osDelay(1000);
	
	//dbg_sw&= ~DBG_BUZ;	//禁止蜂鸣。
	
	
	
	//上电自动清零
	if(0 == strcmp(ITEM_OFF, _tbl_off_on[para_xxx.zero_auto_sw_idx])){
		uartS("\n zero_auto NO");
		return;
	}
	
	//延时2s，以让滤波稳定。
	ts= ts_get();
	while(1){
		ret= osMessageQueueGet(mid_key_disp_io, &msg, 0, 10);
		
		ts_2= ts_get();
		if(2000< ts_del_o_n(ts, ts_2))
			break;
	}
	
	
	ts= ts_get();
	while(1){
		ret= osMessageQueueGet(mid_key_disp_io, &msg, 0, 10);
		
		if(osOK != ret){
			continue;			
		}
		
		if(MSG_TYPE_MV_WT != msg.type){
			continue;
		}	
		
		
		if(dbg_sw & DBG_THREAD_DISP)			
			uartS("\nmsg_mv_wt_pwr_on");
		
		if(msg.dat[0] & LED_STAB_BIT){
			if(msg.wt_div_n<= (cal_ctx.div_n_cap*para_xxx.range_zero/100))
				cnt++;
			else
				cnt= 0;
		} 
		else
			cnt= 0;

		
		/*N秒超时，暂定为2秒。
		*/
		ts_2= ts_get();
		sprintf(str, "\nwt= %d, n=%d, dat0=%02x",msg.wt_div_n, cnt, msg.dat[0]);
		uartS(str);

		
		if(2000< ts_del_o_n(ts, ts_2)){
			if(10<= cnt){	//在2000ms内，应可以收到40个显示消息，这里暂定30满足即可。
				//offset_pwr_on+= -1*msg.wt;	//使用当前显示值
				flag_zero_pwr_on= 1;
				
				uartS("\n zero@pwr on");
				buz_beep(200);
			}
			else{
				sprintf(str,"\n zero_auto err: cnt=%d< 10 or >zero rage", cnt); 
				uartS(str);
			}
			break;	//跳出循环
		}
		else
			uartS("*");
	}
	
	//dbg_sw&= ~DBG_BUZ;	//注释掉，有按键音（蜂鸣）。
}




/*执行串口标定。
若传入的消息不是MV_MT，什么也不做，等下次。
*/
static void do_ser_cal(MSG_U* msg)
{
//	char str[48];
//	
//	
//	if(MSG_TYPE_MV_WT == msg->type){
//		if(stab_arr.mv_stab){
//			//有数据，且显示稳定，则保持该值。
//			//保存旧值。
//			CAL_CTX cal_ctx_bk;

//			cal_save_to_pt(&cal_ctx_bk);
//			
//			switch (op_ser_cal.op_sta){
//				case 1:	//标定零点
//					cal_ctx.pt[0].ad= msg->ad;	
//					cal_ctx.pt[0].load= 0;	
//					cal_ctx.pt[0].cap_cal= fs_max;										//保存表示是的量程。量程可变。
//					cal_ctx.pt[0].div_cal= para_xxx.fd_min;								//保存标定时的分度值，无屌用。
//					cal_ctx.pt[0].k= 9.12345678F;		//固定为9.12345678F，看看float表达为什么，输出显示“9.123457”。
//					cal_ctx.pt[0].uv= msg->uv;
//					
//					//（重新）标定零点对加载点的影响。只简单平移ad，不重新计算load。
//					//TODO需要知道ad的变化量=new-old= pt[0].ad- pt.ad。
//					cal_update_ad(cal_ctx.pt[0].ad- cal_ctx_bk.pt[0].ad);	//更新所有加载点对应的ad值。
//				
//					break;
//				
//				case 2:	//标定加载点1	
//				case 3:	//标定加载点2
//				case 4:	//标定加载点3
//				case 5:	//标定加载点4	
//					cal_ctx.pt[op_ser_cal.op_sta- 1].uv= msg->uv;	//当前实时上报的uv,ad。
//					cal_ctx.pt[op_ser_cal.op_sta- 1].ad= msg->ad;
//					cal_ctx.pt[op_ser_cal.op_sta- 1].used= 1;
//					cal_ctx.pt[op_ser_cal.op_sta- 1].load= op_ser_cal.op_in;
//					cal_ctx.pt[op_ser_cal.op_sta- 1].cap_cal= fs_max;					//保存标定时的量程。量程可变，但此值不变。
//					cal_ctx.pt[op_ser_cal.op_sta- 1].div_cal= para_xxx.fd_min;		//标定时的分度值。分度值可变，但此值不变。
//					cal_ctx.pt[op_ser_cal.op_sta- 1].div_cnt= (int32_t)(cal_ctx.pt[op_ser_cal.op_sta- 1].load/cal_ctx.pt[op_ser_cal.op_sta- 1].div_cal+ 0.5);	//.div_cnt是一个整数。
//				
//					cal_ctx.pt[op_ser_cal.op_sta- 1].k= (cal_ctx.pt[op_ser_cal.op_sta- 1].ad-cal_ctx.pt[op_ser_cal.op_sta- 2].ad);
//					cal_ctx.pt[op_ser_cal.op_sta- 1].k/=(float)(cal_ctx.pt[op_ser_cal.op_sta- 1].load- cal_ctx.pt[op_ser_cal.op_sta- 2].load);		//!!!必须先强制转换为float，否则值由2601.699951变为2601.000000。
//					
//					//将本加载点的值拷贝到之后的所有加载点。
//					cal_update_other_load(op_ser_cal.op_sta- 1);
//						
//					break;				

//				default:	//超过加载点4时，标定加载点4。				
//					break;
//			}
//			
//			
//			//
//			sprintf(str, "\ndo_ser_cal:point[%d], uv=%d, load=%d, ad=%d", op_ser_cal.op_sta-1, cal_ctx.pt[op_ser_cal.op_sta-1].uv, cal_ctx.pt[op_ser_cal.op_sta-1].load, cal_ctx.pt[op_ser_cal.op_sta-1].ad); 
//			uartS(str);
//			
//			//写入FLASH。
//			if(0 == para_to_flash_2(FLASH_UPDATE_MASK_PARA_CAL)){
//				uartS("\nok:save ser_cal's point[]");
//			
//				op_ser_cal.op_sta= 0;	//标定完成：成功。
//				op_ser_cal.op_ret= 0;	//失败原因：无。
//			}
//			else{
//				uartS("\nerr:save ser_cal's point[]");
//				op_ser_cal.op_sta= -1;	//标定完成：失败。
//				op_ser_cal.op_ret= 4;	//失败原因：写FLASH失败。
//			}
//		}
//		else{
//			//不稳定，则不执行标定，即标定失败。
//			op_ser_cal.op_sta= -1;		//标定完成：失败。
//			op_ser_cal.op_ret= 2;		//失败原因：未达到稳定要求。
//			
//			uartS("\ndo ser cal: NOT STAB");
//		}
//	}
//	else{
//		uartS("\n$err:NOT MW_WT");
//	}
}



/*限制点n（1~4）是否满足条件。
返回值：0 ---满足，其他 ---不满足。
输入参数 n: 限制点序号（1~4）
输入参数 val: 待比较的值，即称重值。
!!当判断条件（即PX.3有效条件）为9或10时，须使用上一次满足情况。
*/
static int32_t prepoint_satisfy(uint32_t n, int32_t val)
{
	//static int32_t ret_pre= -1;			//上次状态（0--满足，其他--不满足）
	int32_t val1=0,val2=0;				//用于有符号比较。
	int32_t val_min, val_max;			//用于有符号比较。	
	int32_t val_set, val_thred;			//设定值和阈值
	
	uint32_t tmp32;
	uint16_t cond;
	
	
	if(1 == n){
		tmp32= para_xxx.preset_p1_val_1_hi;
		tmp32<<= 16;
		tmp32|= para_xxx.preset_p1_val_1_lo;
		val1= (int32_t)tmp32;
		
		tmp32= para_xxx.preset_p1_val_2_hi;
		tmp32<<= 16;
		tmp32|= para_xxx.preset_p1_val_2_lo;
		val2= (int32_t)tmp32;
		
		cond= para_xxx.preset_p1_condition;		
	}
	else if(2 == n){
		tmp32= para_xxx.preset_p2_val_1_hi;
		tmp32<<= 16;
		tmp32|= para_xxx.preset_p2_val_1_lo;
		val1= (int32_t)tmp32;
		
		tmp32= para_xxx.preset_p2_val_2_hi;
		tmp32<<= 16;
		tmp32|= para_xxx.preset_p2_val_2_lo;
		val2= (int32_t)tmp32;
		
		cond= para_xxx.preset_p2_condition;
	}
	else if(3 == n){
		tmp32= para_xxx.preset_p3_val_1_hi;
		tmp32<<= 16;
		tmp32|= para_xxx.preset_p3_val_1_lo;
		val1= (int32_t)tmp32;
		
		tmp32= para_xxx.preset_p3_val_2_hi;
		tmp32<<= 16;
		tmp32|= para_xxx.preset_p3_val_2_lo;
		val2= (int32_t)tmp32;
		
		cond= para_xxx.preset_p3_condition;
	}
	else if(4 == n){
		tmp32= para_xxx.preset_p4_val_1_hi;
		tmp32<<= 16;
		tmp32|= para_xxx.preset_p4_val_1_lo;
		val1= (int32_t)tmp32;
		
		tmp32= para_xxx.preset_p4_val_2_hi;
		tmp32<<= 16;
		tmp32|= para_xxx.preset_p4_val_2_lo;
		val2= (int32_t)tmp32;
		
		cond= para_xxx.preset_p4_condition;
	}
	else{
		cond= 0;	
	}
	
	
	if(0 == cond)
			return -1;	//条件是禁止，直接返回-1（即不满足）。
	
	val_min= val1>val2 ? val2:val1;	//取小者。
	val_max= val1>val2 ? val1:val2;	//取大者。
	
	val_set= val1;
	val_thred= val2;
	
	if(1 == cond){
		//<
		if(val< val_min)
			return 0;	//满足，返回0。
		else
			return -1;	//不满足
	}
	else if(2 == cond){
		//<=
		if(val<= val_min)
			return 0;
		else
			return -1;	//不满足
	}
	else if(3 == cond){
		//==
		if(val == val_min)
			return 0;
		else
			return -1;	//不满足
	}	
	else if(4 == cond){
		//>=
		if(val >= val_min)
			return 0;
		else
			return -1;	//不满足
	}
	else if(5 == cond){
		//>
		if(val > val_min)
			return 0;
		else
			return -1;	//不满足
	}
	else if(6 == cond){
		//!=
		if(val != val_min)
			return 0;
		else
			return -1;	//不满足
	}
	else if(7 == cond){
		//__<>__区间外
		if((val < val_min) || (val> val_max))
			return 0;
		else
			return -1;	//不满足
	}
	else if(8 == cond){
		//=<__>=区间内
		if((val_min<= val) || (val<= val_max))
			return 0;
		else
			return -1;	//不满足
	}
	else if(9 == cond){
		//外部触发：暂未实现，直接返回-1。
		//高限值比较
		if(val>= val_set){
			return 0;	//满足
		}
		else if(val< (val_set-val_thred)){
			return -1;
		}
		else{
			//在[val_set-val_thrd, val_set)时。
			if(preset_point[n].sta_cur){
				return 0;	//上次满足，则本次也满足。
			}
			else 
				return -1;	//上次不满足，本次也不满足。
		}
	}
	else if(10 == cond){
		//低限值比较
		if(val<= val_set){
			return 0;	//满足
		}
		else if(val> (val_set+val_thred)){
			return -1;
		}
		else{
			//在(val_set,val_set+val_thrd]时，若上次满足，则本次满足。
			if(preset_point[n].sta_cur){	
				return 0;	//上次满足，则本次也满足。
			}
			else 
				return -1;	//上次不满足，本次也不满足。
		}
	}
	else
		return -1;		//不满足
}






/*限制点n（1~4）的状态。
输入参数 n:   限制点序号（1~4）
输入参数 val: 称重值
说明：非常繁琐的一个函数。TODO如何化繁为简？
      上次状态，本次状态，都是瞬时状态。
      sta_out输出状态是对瞬时状态处理后的状态，不会瞬时变化。
      sta_fin最终状态是由IN1参与，和“开关量测试开关”参与后的状态。
先更新限制点状态，再进行开关量输出（开关量输出和限制点状态是简单关系）。
转变期：从无效变为有效，或者从有效变为无效。
平稳期：一直有效或一直无效。
*/
static void prepoint_sta(uint32_t n, int32_t val)
{
	uint16_t stab_need=0;		//是否判稳
	uint16_t duration_min=0;	//最小持续时间
	uint16_t cond;				//有效条件
	char str[32];
	
	/*限制点的某些变量，不方便用n索引，搞成变量就摆脱n依赖。
	*/
	if(1 == n){
		stab_need= para_xxx.preset_p1_need_stab;			//是否需要稳定。
		duration_min= para_xxx.preset_p1_duration_min;		//需要稳定时的稳定时间，单位是秒，有1位小数。例123是12.3秒。
		cond= para_xxx.preset_p1_condition;					//判断条件。
	}
	else if(2 == n){
		stab_need= para_xxx.preset_p2_need_stab;
		duration_min= para_xxx.preset_p2_duration_min;
		cond= para_xxx.preset_p2_condition;
	}
	else if(3 == n){
		stab_need= para_xxx.preset_p3_need_stab;
		duration_min= para_xxx.preset_p3_duration_min;
		cond= para_xxx.preset_p3_condition;
	}
	else if(4 == n){
		stab_need= para_xxx.preset_p4_need_stab;
		duration_min= para_xxx.preset_p4_duration_min;
		cond= para_xxx.preset_p4_condition;
	}
	
	
	if(0 == cond)
		return;	//“有效条件”为0，直接退出。
	
	
	/*
	有效条件是高、低限值(9,10)时，不需要稳定时间。条件10是迟滞判断，为国外客户定制的。
	*/
	if((9 == cond) || (10 == cond))	
		stab_need= 0;
	

	
	
	/*是否满足条件
	*/
	if(0 == prepoint_satisfy(n, val)){	//满足条件。
		if((dbg_sw & DBG_PRESET_POINT) && (1==n)){
			sprintf(str, "\n\tsatisfy %d",n);
			uartS(str);
		}
		
		//满足指定条件（返回0），本次状态有效。
		preset_point[n].sta_cur= 1;	
		
		if(preset_point[n].flag_wait_unsatisfy){	//是否等待“变为无效”。只有等待无效，才能向下判断。
			//uartS("\ngoto IN_DEAL");
			goto IN_DEAL;	//跳转到IN对限制值的清零处理。
		}
		
		if(0 == preset_point[n].sta_pre){
			//上次条件状态为无效，本次为有效，则为一个转变，需要记录时间。
			preset_point[n].ts_valid_start= ts_get();	//osKernelGetTickCount();
			preset_point[n].ts_valid_start_valid= 1;
			
			preset_point[n].stab_cnt= 0;
			
			if(dbg_sw & DBG_PRESET_POINT){
				sprintf(str, "\n\tinvalid->valid:%d",n);
				uartS(str);
			}
		}
		else{
			//上次有效，本次有效，没有变化。若起始时间戳无效（即已处理过转变期，现在为平稳期），则不处理。
			if(0 == preset_point[n].ts_valid_start_valid)
				goto IN_DEAL;
		}
		
		
		if(stab_need){	
			/*需要条件判稳。
			若已处在平稳期（0 == preset_point[n].ts_valid_start_valid），则什么都不做。肯定是先转变期，再平稳期。
			*/
			if(preset_point[n].ts_valid_start_valid && (duration_min*100< ts_del_o_n(preset_point[n].ts_valid_start, ts_get()))){	//ms值判断。QQ暂未考虑Tick溢出，因为按ms算，32-bit溢出，需要>49d。
				preset_point[n].sta_out= 1;		//输出状态（已经满足：有效条件和持续时间）。
				preset_point[n].ts_valid_start_valid= 0;	//标示为已处理，处于平稳期。
				
				if(dbg_sw & DBG_PRESET_POINT){
					sprintf(str, "\n\t\t\tsta_out=1(stab):%d", n);
					uartS(str);
				}
			}
		}
		else{
			preset_point[n].sta_out= 1;	//直接的状态
			
			if(dbg_sw & DBG_PRESET_POINT){
				sprintf(str, "\n\t\tsta_out=1(no_stab):%d", n);
				uartS(str);
			}
		}
	}
	else{
		if((dbg_sw & DBG_PRESET_POINT)&& (1==n)){
			sprintf(str, "\nUnsatisfy %d",n);
			uartS(str);
		}
		
		preset_point[n].sta_cur= 0;//不满足指定条件，本次状态无效。
		
		if(1 == preset_point[n].sta_pre){
			//由有效变无效。
			preset_point[n].ts_invalid_start= ts_get();	//osKernelGetTickCount();
			preset_point[n].ts_invalid_start_valid= 1;
			
			preset_point[n].stab_cnt= 0;
			
			if(dbg_sw & DBG_PRESET_POINT){
				sprintf(str, "\n\tvalid->invalid:%d", n);
				uartS(str);
			}
		}
		else{
			//上次无效，本次无效，没有变化。若起始时间戳无效（即已处理过转换期，现在为平稳期），则不处理。
			if(0 == preset_point[n].ts_invalid_start_valid)
				goto DO_UNSATISFY;
		}
		
		
		if(stab_need){
			//需要判稳。
			if(preset_point[n].ts_invalid_start_valid && (duration_min*100< ts_del_o_n(preset_point[n].ts_invalid_start, ts_get()))){		
				preset_point[n].sta_out= 0;	//输出状态
				preset_point[n].ts_invalid_start_valid= 0;		//标示为已处理，处于平稳期。
				
				if(dbg_sw & DBG_PRESET_POINT){
					sprintf(str, "\n\t\t\tsta_out=0(stab):%d", n);
					uartS(str);
				}				
			}
		}
		else{
			preset_point[n].sta_out= 0;	//直接的状态
			
			if(dbg_sw & DBG_PRESET_POINT){
				sprintf(str, "\n\t\tsta_out=0(no_stab):%d", n);
				uartS(str);
			}
		}
		
		
DO_UNSATISFY:	
		if(preset_point[n].flag_wait_unsatisfy){
			if(0 == preset_point[n].sta_out){
				preset_point[n].flag_wait_unsatisfy= 0;	//已检测到状态无效，所以清零等待无效标志。
				
				if(dbg_sw & DBG_PRESET_POINT){
					uartS("\n\t\t\t\t0 -->flag_wait_unsatify");
				}
			}
		}
	}
	

IN_DEAL:	//处理IN。	
	preset_point[n].sta_pre= preset_point[n].sta_cur;	//!!每次比较后，都要更新上次状态。
	
	
	if((9 == cond) || (10 == cond)){	
		/*
		有效条件是高、低限值(9,10)时，不受IN1影响，直接输出当前值。
		*/
		preset_point[n].sta_fin= preset_point[n].sta_out;
	}
	else{
		/*再结合IN有效时，对限制点状态的影响。
		IN1定义为清零限制1~4，且IN1有效。!!IN1只用于一个限制点。当IN1有效时，直接将相应限制点置为无效，设定相应限制点为“等待条件不满足”。		
		*/
		//IN1无效（即不清零，不参与），那么可以用.sta_out更新.sta_fin。
		if(0 == preset_point[n].flag_wait_unsatisfy){
			//不在等待“条件无效”，直接赋值
			preset_point[n].sta_fin= preset_point[n].sta_out;
		}
		else{
			//正在等待“条件重回无效”，值不变。
			preset_point[n].sta_fin= preset_point[n].sta_fin;	//.sta_fin不变。
		}
	}
	
	
	/*再结合“开关量测试开关”，才能进一步更新OUT1，OUT2引脚。否则，不更新OUT1，OUT2。
	*/
	if(para_coil & COIL_BIT_MASK_RST_SW){	//开关量测试开关有效。
		//do nothing.
		uartS("\n sw test io");
	}
	else{	//开关量测试开关无效。
		if(dbg_sw & DBG_PRESET_POINT){
			sprintf(str, "\nsta_fin=%d:%d",preset_point[n].sta_fin, n);
			uartS(str);
		}
		
		//先更新寄存器状态。
		if(1 == n){ 
			if(preset_point[n].sta_fin)
				para_coil|= COIL_BIT_MASK_STA_P1;	//para_01_05.preset_1_sta= 1;
			else
				para_coil&= ~COIL_BIT_MASK_STA_P1;	//para_01_05.preset_1_sta= 0;
		}
		else if(2 == n){ 
			if(preset_point[n].sta_fin)
				para_coil|= COIL_BIT_MASK_STA_P2;	//para_01_05.preset_2_sta= 1;
			else
				para_coil&= ~COIL_BIT_MASK_STA_P2;	//para_01_05.preset_2_sta= 0;
		}
		else if(3 == n){ 
			if(preset_point[n].sta_fin)
				para_coil|= COIL_BIT_MASK_STA_P3;	//para_01_05.preset_3_sta= 1;
			else
				para_coil&= ~COIL_BIT_MASK_STA_P3;	//para_01_05.preset_3_sta= 0;
		}
		else if(4 == n){ 
			if(preset_point[n].sta_fin)
				para_coil|= COIL_BIT_MASK_STA_P4;	//para_01_05.preset_4_sta= 1;
			else
				para_coil&= ~COIL_BIT_MASK_STA_P4;	//para_01_05.preset_4_sta= 0;
		}
		
		/*再更新可能的引脚状态（用preset_point[n].used_n不好）。
		只有在out1_define，out2_define中引用了限制点，才能真正地作用到OUT1，OUT2引脚，否则，只更新寄存器状态。
		*/
		if((2+ n) == para_xxx.out1_define_idx){	//2+n= 3,4,5,6.其中3---限制1，4---限制2，5---限制3，6---限制4。
			//OUT1定义和限制n关联
			if(0 == (para_coil & COIL_BIT_MASK_TEST_SW)){	//不在test io。
				if(preset_point[n].sta_fin)
					OUT1_VALID;
				else
					OUT1_INVALID;
			}
		}
		
		if((2+ n) == para_xxx.out2_define_idx){
			if(preset_point[n].sta_fin)
				OUT2_VALID;
			else
				OUT2_INVALID;
		}		
	}
}



///*预置点n（1~4）的状态。
//输入参数 n: 限制点序号，以指定限制点
//输入参数 val: 称重值
//说明：上次状态，本次状态，都是瞬时状态。
//      sta_out输出状态是对瞬时状态处理后的状态，不会瞬时变化。
//      sta_fin最终状态是由IN1参与，和“开关量测试开关”参与后的状态。
//先更新预置点状态，再进行开关量输出（开关量输出和预置点状态是简单关系）。
//*/
//static void prepoint_sta(uint32_t n, int32_t val)
//{
//	uint16_t stab_need=0;		//是否判稳
//	uint16_t duration_min=0;	//最小持续时间
//	char str[32];
//	
//	/*限制点的某些变量，不方便用n索引，搞成变量就摆脱n依赖。
//	*/
//	if(1 == n){
//		stab_need= para_xxx.preset_p1_need_stab;
//		duration_min= para_xxx.preset_p1_duration_min;
//	}
//	else if(2 == n){
//		stab_need= para_xxx.preset_p2_need_stab;
//		duration_min= para_xxx.preset_p2_duration_min;
//	}
//	else if(3 == n){
//		stab_need= para_xxx.preset_p3_need_stab;
//		duration_min= para_xxx.preset_p3_duration_min;
//	}
//	else if(4 == n){
//		stab_need= para_xxx.preset_p4_need_stab;
//		duration_min= para_xxx.preset_p4_duration_min;
//	}
//	
//	
//	/*是否满足条件
//	*/
//	if(0 == prepoint_satisfy(n, val)){
//		if((dbg_sw & DBG_PRESET_POINT) && (1==n)){
//			sprintf(str, "\n\tsatisfy %d",n);
//			uartS(str);
//		}
//		
//		//满足指定条件（返回0），本次状态有效。
//		preset_point[n].sta_cur= 1;	
//		
//		if(preset_point[n].flag_wait_unsatisfy){	//是否等待“变为无效”。只有等待无效，才能向下判断。
//			uartS("\ngoto IN_DEAL");
//			goto IN_DEAL;	//跳转到IN对限制值的清零处理。
//		}
//		
//		if(0 == preset_point[n].sta_pre){
//			//上次条件状态为无效，本次为有效，则为一个转变，需要记录时间。
//			preset_point[n].ts_valid_start= osKernelGetTickCount();
//			preset_point[n].stab_cnt= 0;
//			
//			if(dbg_sw & DBG_PRESET_POINT){
//				sprintf(str, "\n\tinvalid->valid:%d",n);
//				uartS(str);
//			}
//		}
//		
//			
//		if(stab_need){	
//			if(stab_arr.wt_stab){
//				//AD值判稳定，是由AD处理函数决定的，这里仅使用。
//				preset_point[n].stab_cnt++;
//				
//				if(10< preset_point[n].stab_cnt){
//					preset_point[n].stab_cnt= 11;	//不再自增
//					
//					if((dbg_sw & DBG_PRESET_POINT) && (1== n)){
//						sprintf(str, "\n\tsatisfy's sta_cnt ok:%d", n);
//						uartS(str);
//					}
//				}
//			}
//			
//			//需要条件判稳。
//			if(duration_min*100< (osKernelGetTickCount()- preset_point[n].ts_valid_start)){	//ms值判断。QQ暂未考虑Tick溢出，因为按ms算，32-bit溢出，需要>49d。
//				if(10< preset_point[n].stab_cnt){
//					preset_point[n].sta_out= 1;	//输出状态（既要满足持续时间，又要满足稳定次数）。
//					
//					if(dbg_sw & DBG_PRESET_POINT){
//						sprintf(str, "\n\t\t\tsta_out=1(stab):%d", n);
//						uartS(str);
//					}
//				}
//			}
//		}
//		else{
//			preset_point[n].sta_out= 1;	//直接的状态
//			
//			if(dbg_sw & DBG_PRESET_POINT){
//				sprintf(str, "\n\t\tsta_out=1(dire):%d", n);
//				uartS(str);
//			}
//		}
//	}
//	else{
//		if((dbg_sw & DBG_PRESET_POINT)&& (1==n)){
//			sprintf(str, "\nUnsatisfy %d",n);
//			uartS(str);
//		}
//		
//		preset_point[n].sta_cur= 0;//不满足指定条件，本次状态无效。
//		
//		if(1 == preset_point[n].sta_pre){
//			//由有效变无效。
//			preset_point[n].ts_invalid_start= osKernelGetTickCount();
//			preset_point[n].stab_cnt= 0;
//			
//			if(dbg_sw & DBG_PRESET_POINT){
//				sprintf(str, "\n\tvalid->invalid:%d", n);
//				uartS(str);
//			}
//		}
//		
//		if(stab_need){
//			if(stab_arr.wt_stab){
//				preset_point[n].stab_cnt++;
//				
//				if(10< preset_point[n].stab_cnt){
//					preset_point[n].stab_cnt= 11;	//不再自增
//				
//					if((dbg_sw & DBG_PRESET_POINT) && (1== n)){
//						sprintf(str, "\n\tunsatisfy's sta_cnt ok:%d", n);
//						uartS(str);
//					}					
//				}
//			}
//			
//			//需要判稳。
//			if(duration_min*100< (osKernelGetTickCount()- preset_point[n].ts_invalid_start)){	//ms值判断。QQ暂未考虑Tick
//				
//				if(10< preset_point[n].stab_cnt){
//					preset_point[n].sta_out= 0;	//输出状态
//					
//					if(dbg_sw & DBG_PRESET_POINT){
//						sprintf(str, "\n\t\t\tsta_out=0(stab):%d", n);
//						uartS(str);
//					}
//				}					
//			}
//		}
//		else{
//			preset_point[n].sta_out= 0;	//直接的状态
//			
//			if(dbg_sw & DBG_PRESET_POINT){
//				sprintf(str, "\n\t\tsta_out=0(dire):%d", n);
//				uartS(str);
//			}
//		}
//		
//		
//		if(preset_point[n].flag_wait_unsatisfy){
//			if(0 == preset_point[n].sta_out){
//				preset_point[n].flag_wait_unsatisfy= 0;	//已检测到状态无效，所以清零等待无效标志。
//				
//				uartS("\n\t\t\t\t0 -->flag_wait_unsatify");
//			}
//		}
//	}
//	

//IN_DEAL:	
//	preset_point[n].sta_pre= preset_point[n].sta_cur;
//	
//	/*再结合上IN有效时，对限制点状态的影响。
//	IN1定义为清零限制1~4，且IN1有效。		
//	*/
//	if((1< para_xxx.in1_define)&&(para_xxx.in1_define< 6)){
//		if(in_sta){
//			if((2 == para_xxx.in1_define) && (1 == n)){			//IN1定义为：清零限制点1。		
//				preset_point[n].sta_fin= 0;
//				preset_point[n].flag_wait_unsatisfy= 1;
//				uartS("\nIN1 valid");
//			}
//			else if((3 == para_xxx.in1_define) && (2 == n)){	//IN1定义为：清零限制点2。
//				preset_point[n].sta_fin= 0;
//				preset_point[n].flag_wait_unsatisfy= 1;
//			}
//			else if((4 == para_xxx.in1_define) && (3 == n)){	//IN1定义为：清零限制点3。
//				preset_point[n].sta_fin= 0;
//				preset_point[n].flag_wait_unsatisfy= 1;
//			}
//			else if((5 == para_xxx.in1_define) && (4 == n)){	//IN1定义为：清零限制点4。
//				preset_point[n].sta_fin= 0;
//				preset_point[n].flag_wait_unsatisfy= 1;
//			}
//		}
//		else{
//			//IN1无效，不参与。
//			if(0 == preset_point[n].flag_wait_unsatisfy)
//				preset_point[n].sta_fin= preset_point[n].sta_out;
//		}
//	}
//	else{	
//		//IN1不参与。
//		preset_point[n].sta_fin= preset_point[n].sta_out;	
//	}
//	
//	
//	/*再结合“开关量测试开关”，才能进一步更新OUT1，OUT2引脚。否则，不更新OUT1，OUT2。
//	*/
//	if(para_coil & COIL_BIT_MASK_RST_SW){
//		//do nothing.
//		uartS("\n sw test io");
//	}
//	else{
//		if(dbg_sw & DBG_PRESET_POINT){
//			sprintf(str, "\nsta_fin=%d:%d",preset_point[n].sta_fin, n);
//			uartS(str);
//		}
//		//先更新寄存器状态
//		if(1 == n){ 
//			if(preset_point[n].sta_fin)
//				para_01_05.preset_1_sta= 1;
//			else
//				para_01_05.preset_1_sta= 0;
//		}
//		else if(2 == n){ 
//			if(preset_point[n].sta_fin)
//				para_01_05.preset_2_sta= 1;
//			else
//				para_01_05.preset_2_sta= 0;
//		}
//		else if(3 == n){ 
//			if(preset_point[n].sta_fin)
//				para_01_05.preset_3_sta= 1;
//			else
//				para_01_05.preset_3_sta= 0;
//		}
//		else if(4 == n){ 
//			if(preset_point[n].sta_fin)
//				para_01_05.preset_4_sta= 1;
//			else
//				para_01_05.preset_4_sta= 0;
//		}
//		
//		/*再更新可能的引脚状态（用preset_point[n].used_n不好）。
//		只有在out1_define，out2_define中引用了限制点，才能正真的作用到OUT1，OUT2引脚，否则，只更改限制点状态。
//		*/
//		if((2+ n) == para_xxx.out1_define){
////			if(dbg_sw & DBG_PRESET_POINT){
////				uartS("\n out1_define");
////			}
//			//OUT1定义和限制n关联
//			if(preset_point[n].sta_fin)
//				OUT1_VALID;
//			else
//				OUT1_INVALID;
//		}
//		
//		if((2+ n) == para_xxx.out2_define){
////			if(dbg_sw & DBG_PRESET_POINT){
////				uartS("\n out2_define");
////			}
//			if(preset_point[n].sta_fin)
//				OUT2_VALID;
//			else
//				OUT2_INVALID;
//		}		
//	}
//}

static void spoint_config(void)
{		
	preset_point[1].sta_pre= 0;					//不满足
	preset_point[1].sta_cur= 0;					//不满足
	preset_point[1].flag_wait_unsatisfy= 0;		//不是”等待变为不满足”。
	preset_point[1].ts_valid_start_valid= 0;	//时间戳无效
	preset_point[1].ts_invalid_start_valid= 0;	//时间戳无效

	preset_point[2].sta_pre= 0;
	preset_point[2].sta_cur= 0;
	preset_point[2].flag_wait_unsatisfy= 0;
	preset_point[2].ts_valid_start_valid= 0;
	preset_point[2].ts_invalid_start_valid= 0;

	preset_point[3].sta_pre= 0;
	preset_point[3].sta_cur= 0;
	preset_point[3].flag_wait_unsatisfy= 0;
	preset_point[3].ts_valid_start_valid= 0;
	preset_point[3].ts_invalid_start_valid= 0;

	preset_point[4].sta_pre= 0;
	preset_point[4].sta_cur= 0;
	preset_point[4].flag_wait_unsatisfy= 0;
	preset_point[4].ts_valid_start_valid= 0;
	preset_point[4].ts_invalid_start_valid= 0;
}	



/*更新零区状态。
当满足在指定时长，称重值连续在零区值内，零区复位状态有效。
当一个称重值不在零区值内，零区状态无效。
但一个称重值在零区值内，若零区状态有效，则零区复位状态不变。
*/
static void zz_det_update(ZZONE_DET* p, int32_t wt)
{
//	char str[48];
//	sprintf(str, "\nsta_pre:%d sta:%d", p->sta_rst_pre, p->sta_rst);
//	uartS(str);
	
	
	if(wt<= para_self.zzone_val)
		p->sta_wt= 1;	//在零区值内。
	else
		p->sta_wt= 0;	//不在零区值内。
	
	if((0 == p->sta_wt_pre) && (0 != p->sta_wt)){
		//发生转换，记录时间。
		p->ts= ts_get();
	}
	
	if(0 != p->sta_wt){
		if((1000*para_self.zzone_tmo)<= ts_del_o_n(p->ts, ts_get())){
			p->sta_rst_pre= p->sta_rst;
			p->sta_rst= STA_RST_ZZ;
			
			if(STA_RST_LOAD == p->sta_rst_pre){
				if(dbg_sw & DBG_LAJI)
					uartS("\n$zzone valid");
			}
			
			//p->sta_rst_pre= p->sta_rst;	//放在这里不对。
		}
		else{
			/*复位状态不变：
			a. 本次在零区值内，但连续时长不够，复位状态不变。
			b. 复位状态有效，本次在零区值内，复位状态不变，仍有效。
			*/
		}
	}
	else{
		//当前不满足零区值，零区复位状态立即无效。
		p->sta_rst_pre= p->sta_rst;
		p->sta_rst= STA_RST_LOAD;
		
		if(STA_RST_ZZ == p->sta_rst_pre){
			if(dbg_sw & DBG_LAJI)
				uartS("\nzzone invalid");
		}
		
		//p->sta_rst_pre= p->sta_rst;
	}
	
	p->sta_wt_pre= p->sta_wt;		
}

/************************ END OF FILE ************************/
